﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace SharpBroadcast
{
    public class OggHandler : MediaHandler // just for experimental purpose ...
    {
        public readonly byte[] MAGIC_TAG = { Convert.ToByte('O'), Convert.ToByte('g'), Convert.ToByte('g'), Convert.ToByte('S') };
        public const int MAGIC_TAG_LEN = 4;

        public const int MAX_CACHE_SIZE = 1024 * 1024;

        public OggHandler()
        {
            // ...
        }

        private byte[] GenAudioData(List<byte> header, List<List<byte>> chunks)
        {
            List<byte> finalData = new List<byte>();

            if (header.Count > 0) finalData.AddRange(header);

            foreach (var item in chunks)
            {
                finalData.AddRange(item);
            }

            return finalData.ToArray();
        }

        public override void HandleInput(HttpListenerContext ctx)
        {
            try
            {
                if (ctx == null || MediaServer == null) return;
                if (ctx.Request.Url.Segments.Length <= 1) return;

                List<object> clients = null;
                string sourceName = ctx.Request.Url.Segments[1].Replace("/", "");
                string address = ctx.Request.RemoteEndPoint.ToString();

                MediaDispatcher dispatcher = null;

                bool foundFirstMagicTag = false;

                List<byte> headerData = new List<byte>();

                List<List<byte>> currentChunks = new List<List<byte>>();

                //int totalTimes = 0;
                int totalBytes = 0;

                int currentByte = 0;

                List<byte> cache = new List<byte>();

                try
                {
                    while (MediaServer.IsWorking())
                    {
                        currentByte = ctx.Request.InputStream.ReadByte();
                        if (currentByte < 0) break;

                        bool foundTag = false;

                        if (currentByte == MAGIC_TAG.Last() && cache.Count >= MAGIC_TAG_LEN - 1)
                        {
                            foundTag = true;
                            for (var i = 1; i <= MAGIC_TAG_LEN - 1; i++)
                            {
                                if (cache[cache.Count - i] != MAGIC_TAG[MAGIC_TAG_LEN - 1 - i])
                                {
                                    foundTag = false;
                                    break;
                                }
                            }
                            if (foundTag) cache.RemoveRange(cache.Count - (MAGIC_TAG_LEN - 1), MAGIC_TAG_LEN - 1);
                        }

                        if (foundTag)
                        {
                            if (!foundFirstMagicTag)
                            {
                                foundFirstMagicTag = true;

                                if (sourceName.Length > 0)
                                {
                                    // update state

                                    MediaChannelState state = new MediaChannelState();
                                    state.SourceName = sourceName;
                                    state.SourceAddress = address;
                                    state.MediaSize = "ogg";
                                    state.ClientCount = 0;

                                    MediaServer.UpdateState(sourceName, state);

                                    // get clients
                                    clients = MediaServer.GetClients(sourceName);
                                }

                                if (clients == null) return;
                                else if (dispatcher == null) dispatcher = new MediaDispatcher(MediaServer, clients);
                            }
                            else
                            {
                                currentChunks.Add(new List<byte>(cache));
                                totalBytes += cache.Count;
                                if (totalBytes >= MediaServer.OutputBufferSize)
                                {
                                    if (headerData.Count <= 0 && currentChunks.Count > 1)
                                    {
                                        headerData.AddRange(currentChunks[0]);
                                        headerData.AddRange(currentChunks[1]);
                                        currentChunks.RemoveRange(0, 2);
                                    }
                                    byte[] data = GenAudioData(headerData, currentChunks);
                                    if (dispatcher != null)
                                    {
                                        BufferData buffer = new BufferData(data, data.Length);
                                        dispatcher.Buffers.Enqueue(buffer);
                                        dispatcher.Process();
                                    }

                                    //totalTimes++;
                                    //File.WriteAllBytes("C:/test/res/test/" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + totalTimes + ".ogg", data);

                                    currentChunks.Clear();
                                    totalBytes = 0;
                                }
                            }

                            cache.Clear();
                            cache.AddRange(MAGIC_TAG);
                        }
                        else
                        {
                            cache.Add((byte)currentByte);
                        }

                        if (cache.Count > MAX_CACHE_SIZE)
                        {
                            MediaServer.Logger.Error("Exceeded max cache size when read audio input.");
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    MediaServer.Logger.Error("HTTP input error: " + ex.Message);
                }

                if (dispatcher != null) dispatcher.Stop();

                try
                {
                    ctx.Response.Close();
                }
                catch (Exception ex)
                {
                    MediaServer.Logger.Error("HTTP context error: " + ex.Message);
                }

                if (sourceName.Length > 0)
                {
                    // update state
                    MediaServer.RemoveState(sourceName);
                }
            }
            catch (Exception ex)
            {
                MediaServer.Logger.Error("HTTP process error in thread: " + ex.Message);
            }
        }
    }
}
